/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package digitaldreamers.games.ddmonsters.servers.gameserver.components;

import java.util.ArrayList;

/**
 * This class presents a room.
 * @author DaoLe
 */
public class RoomComponent {
    
    // <editor-fold defaultstate="collapsed" desc="Constants">
    
    // <editor-fold defaultstate="collapsed" desc="Error messages">
    private static final String ROOM_CREATOR_IS_NULL_ERROR_MESSAGE = "Room creator can not be null.";
    private static final String ROOM_GUEST_IS_NULL_ERROR_MESSAGE = "Room guest can not be null.";
    private static final String ROOM_RULE_IS_NULL_ERROR_MESSAGE = "Room rule can not be null.";
    private static final String PLAYER_IS_NULL_ERROR_MESSAGE = "Player can not be null.";
    private static final String ROOM_GUEST_IS_WRONG_ERROR_MESSAGE = "This player is not the room guest.";
    private static final String ROOM_MASTER_IS_WRONG_ERROR_MESSAGE = "This player is not the room master.";
    // </editor-fold>
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="IOnRoomStateChangedListener">

    /**
     * Definition interface for a callback to be invoked when a room state changed.
     */
    public static interface IOnRoomStateChangedListener {
        /**
         * The callback function. It is called when a room state changed.
         * @param room The room of which state changed.
         */
        public void onRoomStateChanged(RoomComponent room);
    }
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="IOnRoomCreatedListener">
    
    /**
     * Definition interface for a callback to be invoked when a room is created.
     */
    public static interface IOnRoomCreatedListener {
        /**
         * The callback function. It is called when a room is created.
         * @param room The new room
         */
        public void onRoomCreated(RoomComponent room);
    }
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room state enum">
    public static enum RoomStates {
        CREATED(0),
        WAITING(1),
        FULL(2),
        REMOVED(3);
        
        private int m_iState;
 
        private RoomStates(int iState) {
            this.m_iState = iState;
        }
 
        public int getState() {
            return m_iState;
        }
    }
    
    public static enum FullSubstates {
        NOT_READY(0),
        READY(1),
        PLAYING(2);
        
        private int m_iFullSubstate;
        
        private FullSubstates(int iFullSubstate) {
            this.m_iFullSubstate = iFullSubstate;
        }
 
        public int getFullSubstate() {
            return m_iFullSubstate;
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room rule enum">
    /*
    public static enum RoomRules {
        NORMAL(0),
        MONEY(1),
        CARD(2);
        
        private int m_iRule;
        
        private RoomRules(int iRule) {
            this.m_iRule = iRule;
        }
 
        public int getRule() {
            return m_iRule;
        }
    }
    */
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Static variable members">
    
    //Listener on the room creation event.
    private static ArrayList<IOnRoomCreatedListener> m_RoomCreatedListeners = new ArrayList<>();
    
    //Automatically increased ID.
    private static long s_m_lNewRoomID = 0;
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Member variables">
    private long m_lRoomID = 0;//ID of the room.
    private DuelComponent m_Duel = null;//The duel in the room.
    private PlayerComponent m_RoomMaster = null;//The room master.
    private PlayerComponent m_RoomGuest = null;//The guest that joined the room.
    private RoomStates m_RoomState = RoomStates.CREATED;//The current state of the room. CREATED is the default state.
    private FullSubstates m_FullSubstate = null;//Substate of the room when it is in FULL state.
    private ArrayList<IOnRoomStateChangedListener> m_RoomStateChangedListeners = null;//Listeners on the room state change event.
    //private RoomRules m_RoomRule = null;//The rule of the room. Default is NORMAL.
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Get/Set methods">
    
    // <editor-fold defaultstate="collapsed" desc="Room ID">
    /**
     * Get the room ID.
     * @return The room ID.
     */
    public long getRoomID() {
        return m_lRoomID;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room master">
    /**
     * Get the room master.
     * @return The master of the room.
     */
    public PlayerComponent getRoomMaster() {
        return m_RoomMaster;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room guest">
    /**
     * Get the guest player of the room.
     * @return The guest player.
     */
    public PlayerComponent getRoomGuest() {
        return m_RoomGuest;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Player opponent">
    /**
     * Get the opponent of one player in the room.
     * @param player The player to get the opponent.
     * @return The opponent of the player.
     */
    public PlayerComponent getOpponent(PlayerComponent player) {
        if(player == m_RoomMaster)
            return m_RoomGuest;
        else if(player == m_RoomGuest)
            return m_RoomMaster;
        return null;    
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room state">
    /**
     * Get the current state of the room.
     * @return The current state of the room.
     */
    public RoomStates getRoomState() {
        return m_RoomState;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room's substate of "Full" state">
    /**
     * Get the substate of the room when it is in "Full" state.
     * @return The substate when the room is full.
     */
    public FullSubstates getFullSubstate() {
        if(m_RoomState == RoomStates.FULL) {
            return m_FullSubstate;
        }
        else {
            return null;
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Room rule">
    /**
     * Get the current rule of the room.
     * @return The current rule of the room.
     */
    //public RoomRules getRoomRule() {
        //return roomRule;
    //}
    
    /**
     * Set the rule of the room.
     * @param roomRule The rule of the room.
     */
    //public void setRoomRule(RoomRules roomRule) {
        //this.roomRule = roomRule;
    //}
    // </editor-fold>
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Constructors">
    
    // <editor-fold defaultstate="collapsed" desc="RoomComponent(long lRoomID, PlayerComponent roomMaster, RoomRules roomRule)">
    /**
     * Instantiate a new room.
     * @param lRoomID The room ID.
     * @param roomMaster The room creator.
     * @param roomRule The rule of the room.
     */
    public RoomComponent(long lRoomID, PlayerComponent roomMaster/*, RoomRules roomRule*/) {
        m_lRoomID = lRoomID;
        m_RoomMaster = roomMaster;
        m_RoomStateChangedListeners = new ArrayList<>();
        //this.roomRule = roomRule;
        roomMaster.setRoom(this);
        m_RoomState = RoomStates.WAITING;
    }
    // </editor-fold>
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Static methods">
    
    // <editor-fold defaultstate="collapsed" desc="createRoom(PlayerComponent creator, RoomRules roomRule)">
    /**
     * Create a room.
     * @param creator The room creator.
     * @param roomRule The rule of the room.
     * @return The result of the "create room" action. True if the player successfully create a room. Otherwise, false.
     * @throws IllegalArgumentException Exception thrown when the room creator or room rule is null.
     */
    public static boolean createRoom(PlayerComponent creator/*, RoomRules roomRule*/) throws IllegalArgumentException {
        if (creator == null) {
            throw new IllegalArgumentException(ROOM_CREATOR_IS_NULL_ERROR_MESSAGE);
        }
        //if (roomRule == null) {
            //throw new IllegalArgumentException(ROOM_RULE_IS_NULL_ERROR_MESSAGE);
        //}
        RoomComponent room = new RoomComponent(s_m_lNewRoomID ++, creator/*, roomRule*/);
        fireRoomCreatedEvent(room);
        return true;
    }
    // </editor-fold>
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Methods">
    
    /**
     * Call this function when a player attempts to join the room.
     * @param guest The player who is joining the room.
     * @return The result of the "join room" action: True if the player successfully joined the room. Otherwise, false.
     * @throws IllegalArgumentException Exception thrown when the guest is null.
     */
    public boolean joinRoom(PlayerComponent guest) throws IllegalArgumentException {
        if (guest == null) {
            throw new IllegalArgumentException(ROOM_GUEST_IS_NULL_ERROR_MESSAGE);
        }
        if(m_RoomState == RoomStates.WAITING) {
            m_RoomState = RoomStates.FULL;
            m_FullSubstate = FullSubstates.NOT_READY;
            m_RoomGuest = guest;
            m_RoomGuest.setRoom(this);
            fireRoomStateChangedEvent(this);
            return true;
        }
        else {
            return false;
        }
    }
    
    /**
     * Call this function when a player leaves the room. It supports switching room master.
     * @param player The player who leaves the room.
     * @return The result of the "leave room" action: True if the player successfully left the room. Otherwise, false.
     * @throws IllegalArgumentException Exception thrown when the player is null.
     */
    public boolean leaveRoom(PlayerComponent player) throws IllegalArgumentException {
        if (player == null) {
            throw new IllegalArgumentException(PLAYER_IS_NULL_ERROR_MESSAGE);
        }
        
        boolean bResult = false;
        
        //If the player is the guest, just let him leave the room.
        if(player == m_RoomGuest) {
            m_RoomGuest = null;
            m_RoomState = RoomStates.WAITING;
            m_FullSubstate = null;
            bResult = true;
        }
        
        //If the player is the room master, check if master switching is needed.
        else if (player == m_RoomMaster) {
            m_RoomMaster = null;
            
            //If there is no guest, set the room to "Removed" state.
            if(m_RoomGuest == null) {
                m_RoomState = RoomStates.REMOVED;
                m_FullSubstate = null;
            }
            
            //Otherwise, make the guest the room owner.
            else {
                m_RoomMaster = m_RoomGuest;
                m_RoomGuest = null;
                m_RoomState = RoomStates.WAITING;
                m_FullSubstate = null;
            }
            
            bResult = true;
        }
        
        if (bResult) {
            
            //Set the player not in a duel.
            player.setDuel(null);
            
            //Set the player not in a room.
            player.setRoom(null);
        }
        
        //Fire the room state change event.
        fireRoomStateChangedEvent(this);
        
        return bResult;
    }
    
    /**
     * Call this function when the guest says he is un/ready to play.
     * @param guest The room guest.
     * @param bIsReady  The ready status of the guest.
     * @return The result of the "ready" action: True if it is done. Otherwise, false.
     * @throws IllegalArgumentException Exception thrown when the guest is null or is not the room guest.
     */
    public boolean ready(PlayerComponent guest, boolean bIsReady) throws IllegalArgumentException
    {
        if (guest == null || guest != m_RoomGuest) {
            throw new IllegalArgumentException(ROOM_GUEST_IS_WRONG_ERROR_MESSAGE);
        }
        if (m_RoomState == RoomStates.FULL) {
            if(bIsReady) {
                m_FullSubstate = FullSubstates.READY;
            }
            else {
                m_FullSubstate = FullSubstates.NOT_READY;
            }
            return true;
        }
        return false;
    }
    
    /**
     * Call this function when the room master wants to start the duel.
     * @param master The room master.
     * @return The result of the "start duel" action: True if it is done. Otherwise, false.
     * @throws IllegalArgumentException Exception thrown when the master is null or is not the room master.
     */
    public boolean startDuel(PlayerComponent master) throws IllegalArgumentException {
        if (master == null || master != m_RoomMaster) {
            throw new IllegalArgumentException(ROOM_MASTER_IS_WRONG_ERROR_MESSAGE);
        }
        if (m_RoomState == RoomStates.FULL && m_FullSubstate == FullSubstates.READY) {
            m_FullSubstate = FullSubstates.PLAYING;
            if(m_Duel == null) {
                m_Duel = new DuelComponent(this);
            }
            else {
                m_Duel.initialize();
            }
            return true;
        }
        return false;
    }
    
    /**
     * Register a new listener for the room state change event.
     * @param listener The new listener of the event.
     * @return The result whether or not the listener is registered.
     */
    public boolean addRoomStateChangedListener(IOnRoomStateChangedListener listener) {
        
        // Only register new listeners.
        if (!m_RoomStateChangedListeners.contains(listener)) {
            return m_RoomStateChangedListeners.add(listener);
        }
        return false;
    }
    
    /**
     * Unregister a listener for the room state change event.
     * @param listener The registered listener of the event.
     * @return The result whether or not the listener is unregistered.
     */
    public boolean removeRoomStateChangedListener(IOnRoomStateChangedListener listener) {
        
        // Only remove registered listener.
        if (m_RoomStateChangedListeners.contains(listener)) {
            return m_RoomStateChangedListeners.remove(listener);
        }
        return false;
    }
    
    /**
     * Fire the room state change event.
     * @param room The room of which state changed.
     */
    private void fireRoomStateChangedEvent(RoomComponent room) {
        IOnRoomStateChangedListener[] listeners = new IOnRoomStateChangedListener[m_RoomStateChangedListeners.size()];
        listeners = m_RoomStateChangedListeners.toArray(listeners);
        for (IOnRoomStateChangedListener listener : listeners) {
            listener.onRoomStateChanged(room);
        }
    }
    
    /**
     * Register a new listener for the room creation event.
     * @param listener The new listener of the event.
     * @return The result whether or not the listener is registered.
     */
    public static boolean addRoomCreatedListener(IOnRoomCreatedListener listener) {
        
        // Only register new listeners.
        if (!m_RoomCreatedListeners.contains(listener)) {
            return m_RoomCreatedListeners.add(listener);
        }
        return false;
    }
    
    /**
     * Unregister a listener for the room creation event.
     * @param listener The registered listener of the event.
     * @return The result whether or not the listener is unregistered.
     */
    public static boolean removeRoomCreatedListener(IOnRoomCreatedListener listener) {
        
        // Only remove registered listener.
        if (m_RoomCreatedListeners.contains(listener)) {
            return m_RoomCreatedListeners.remove(listener);
        }
        return false;
    }
    
    /**
     * Fire the room creation event.
     * @param room The new room.
     */
    private static void fireRoomCreatedEvent(RoomComponent room) {
        IOnRoomCreatedListener[] listeners = new IOnRoomCreatedListener[m_RoomCreatedListeners.size()];
        listeners = m_RoomCreatedListeners.toArray(listeners);
        for(IOnRoomCreatedListener listener : listeners) {
            listener.onRoomCreated(room);
        }
    }
    
    // </editor-fold>
}
